package com.ikingtech.framework.sdk.approve.embedded;

import com.ikingtech.framework.sdk.approve.model.*;
import com.ikingtech.framework.sdk.approve.model.rpc.ApproveFormBeanDefinition;
import com.ikingtech.framework.sdk.approve.model.rpc.ApproveFormBeanDefinitionRegisterParam;
import com.ikingtech.framework.sdk.approve.model.rpc.ApproveFormWidgetBeanDefinition;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.approve.ApproveFormTypeEnum;
import com.ikingtech.framework.sdk.enums.common.FrameworkAgentTypeEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.support.agent.FrameworkAgentProxy;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author tie yan
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ApproveOps {

    public static ApproveFormInstanceBasicDTO submit(ApproveFormInstancePreviewParamDTO previewParam) {
        R<Object> previewResult = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_PREVIEW, previewParam);
        if (!previewResult.isSuccess()) {
            throw new FrameworkException(previewResult.getMsg());
        }
        ApproveFormInstanceDTO submitParam = (ApproveFormInstanceDTO) previewResult.getData();
        submitParam.setBusinessDataId(previewParam.getBusinessDataId());
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_SUBMIT, submitParam);
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
        return (ApproveFormInstanceBasicDTO) result.getData();
    }

    public static ApproveFormInstanceBasicDTO pass(ApproveOpsArgs args) {
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_PASS, commonOperation(args));
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
        return (ApproveFormInstanceBasicDTO) result.getData();
    }

    public static void passBatchAsync(List<ApproveOpsArgs> args) {
        FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_PASS, commonOperation(args));
    }

    public static ApproveFormInstanceBasicDTO reject(ApproveOpsArgs args) {
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_REJECT, commonOperation(args));
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
        return (ApproveFormInstanceBasicDTO) result.getData();
    }

    public static ApproveFormInstanceBasicDTO back(ApproveBackOpsArgs args) {
        ApproveFormOperationDTO operation = commonOperation(args);
        operation.setBackToInstanceNodeId(args.getBackToInstanceNodeId());
        operation.setBackToInstanceNodeUserIds(args.getBackToInstanceNodeUserIds());
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_BACK, operation);
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
        return (ApproveFormInstanceBasicDTO) result.getData();
    }

    public static ApproveFormInstanceBasicDTO reSubmit(ApproveOpsArgs args) {
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_RE_SUBMIT, commonOperation(args));
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
        return (ApproveFormInstanceBasicDTO) result.getData();
    }

    public static void register(String code, String businessType, String name, Map<Object, Object> jsonMap) {
        ApproveFormBeanDefinitionRegisterParam reportParam = new ApproveFormBeanDefinitionRegisterParam();
        ApproveFormBeanDefinition beanDefinition = new ApproveFormBeanDefinition();
        if (Tools.Coll.isBlankMap(jsonMap) || !jsonMap.containsKey("widgetList")) {
            return;
        }
        List<Object> widgetList = findWidgetList(Tools.Obj.objList(jsonMap.get("widgetList")));
        beanDefinition.setWidgets(Tools.Coll.convertList(widgetList, widget -> {
            Map<String, Object> widgetMap = Tools.Json.objToMap(widget);
            ApproveFormWidgetBeanDefinition widgetBeanDefinition = new ApproveFormWidgetBeanDefinition();
            widgetBeanDefinition.setWidgetCode((String) widgetMap.get("id"));
            widgetBeanDefinition.setWidgetType(((String) widgetMap.get("type")).replace("-", "_").toUpperCase());

            Map<String, Object> widgetOption = Tools.Json.objToMap(widgetMap.get("options"));
            widgetBeanDefinition.setWidgetName((String) widgetOption.get("keyName"));
            widgetBeanDefinition.setWidgetLabel((String) widgetOption.get("label"));
            widgetBeanDefinition.setRequired((Boolean) widgetOption.get("required"));
            widgetBeanDefinition.setHidden((Boolean) widgetOption.get("hidden"));
            return widgetBeanDefinition;
        }));
        beanDefinition.setType(ApproveFormTypeEnum.PROCESS_ONLY);
        beanDefinition.setName(name);
        beanDefinition.setVisible(true);
        beanDefinition.setFormJson(Tools.Json.toJsonStr(jsonMap));
        beanDefinition.setCode(code);
        beanDefinition.setBusinessType(businessType);
        reportParam.setBeanDefinitions(Tools.Coll.newList(beanDefinition));
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_FORM_REGISTER, reportParam);
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
    }

    public static void unregister(String businessType) {
        R<Object> result = FrameworkAgentProxy.agent().execute(FrameworkAgentTypeEnum.APPROVE_FORM_UNREGISTER, businessType);
        if (!result.isSuccess()) {
            throw new FrameworkException(result.getMsg());
        }
    }

    private static List<ApproveFormOperationDTO> commonOperation(List<ApproveOpsArgs> args) {
        return Tools.Coll.convertList(args, ApproveOps::commonOperation);
    }

    private static ApproveFormOperationDTO commonOperation(ApproveOpsArgs args) {
        ApproveFormOperationDTO operation = new ApproveFormOperationDTO();
        operation.setFormInstanceId(args.getFormInstanceId());
        operation.setFormData(args.getFormData());
        operation.setApproveComment(args.getApproveComment());
        operation.setAttachments(Tools.Coll.convertList(args.getAttachments(), attachmentArgs -> Tools.Bean.copy(attachmentArgs, ApproveRecordNodeAttachmentDTO.class)));
        operation.setImages(args.getImages());
        return operation;
    }

    private static List<Object> findWidgetList(List<Object> widgetListNode) {
        List<Object> result = new ArrayList<>();
        for (Object subNode : widgetListNode) {
            Map<String, Object> subNodeMap = Tools.Json.objToMap(subNode);
            if ("button".equals(subNodeMap.get("type")) ||
                    "vf-dialog".equals(subNodeMap.get("type")) ||
                    "ik-upload-file".equals(subNodeMap.get("type"))) {
                continue;
            }
            if (subNodeMap.containsKey("widgetList")) {
                if (!"sub-form".equals(subNodeMap.get("type"))) {
                    result.addAll(findWidgetList(Tools.Obj.objList(subNodeMap.get("widgetList"))));
                }
            } else if (subNodeMap.containsKey("cols")) {
                List<Object> gridSubNodes = Tools.Obj.objList(subNodeMap.get("cols"));
                gridSubNodes.forEach(gridSubNode -> {
                    Map<String, Object> gridSubNodeMap = Tools.Json.objToMap(gridSubNode);
                    if (gridSubNodeMap.containsKey("widgetList")) {
                        result.addAll(findWidgetList(Tools.Obj.objList(gridSubNodeMap.get("widgetList"))));
                    }
                });
            } else if (subNodeMap.containsKey("tabs")) {
                List<Object> tabSubNodes = Tools.Obj.objList(subNodeMap.get("tabs"));
                tabSubNodes.forEach(tabSubNode -> {
                    Map<String, Object> tabSubNodeMap = Tools.Json.objToMap(tabSubNode);
                    if (tabSubNodeMap.containsKey("widgetList")) {
                        result.addAll(findWidgetList(Tools.Obj.objList(tabSubNodeMap.get("widgetList"))));
                    }
                });
            } else {
                result.add(subNode);
            }
        }
        return result;
    }
}
